Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | /** * API route for approving parsed worksheet results and adding to existing session * * POST /api/curriculum/[playerId]/attachments/[attachmentId]/approve * - Approves the parsing result * - Adds the parsed problems to the EXISTING session that the attachment belongs to * - Does NOT create a new session - attachments are already associated with sessions */ import { NextResponse } from 'next/server' import { eq } from 'drizzle-orm' import { db } from '@/db' import { practiceAttachments } from '@/db/schema/practice-attachments' import { sessionPlans, type SlotResult } from '@/db/schema/session-plans' import { canPerformAction } from '@/lib/classroom' import { getUserId } from '@/lib/viewer' import { convertToSlotResults, computeParsingStats } from '@/lib/worksheet-parsing' import { withAuth } from '@/lib/auth/withAuth' /** * POST - Approve parsing and add problems to existing session */ export const POST = withAuth(async (_request, { params }) => { try { const { playerId, attachmentId } = (await params) as { playerId: string; attachmentId: string } if (!playerId || !attachmentId) { return NextResponse.json({ error: 'Player ID and Attachment ID required' }, { status: 400 }) } // Authorization check const userId = await getUserId() const canApprove = await canPerformAction(userId, playerId, 'start-session') if (!canApprove) { return NextResponse.json({ error: 'Not authorized' }, { status: 403 }) } // Get attachment record const attachment = await db .select() .from(practiceAttachments) .where(eq(practiceAttachments.id, attachmentId)) .get() if (!attachment) { return NextResponse.json({ error: 'Attachment not found' }, { status: 404 }) } if (attachment.playerId !== playerId) { return NextResponse.json({ error: 'Attachment not found' }, { status: 404 }) } // Check if already processed if (attachment.sessionCreated) { return NextResponse.json( { error: 'Problems from this worksheet already added to session', }, { status: 400 } ) } // Get the existing session that this attachment belongs to const existingSession = await db .select() .from(sessionPlans) .where(eq(sessionPlans.id, attachment.sessionId)) .get() if (!existingSession) { return NextResponse.json( { error: 'Associated session not found', }, { status: 404 } ) } // Get the parsing result to convert (prefer approved result, fall back to raw) const parsingResult = attachment.approvedResult ?? attachment.rawParsingResult if (!parsingResult) { return NextResponse.json( { error: 'No parsing results available. Parse the worksheet first.', }, { status: 400 } ) } // Convert to slot results // Always use part 1 for offline worksheets - slot indices track individual problems const conversionResult = convertToSlotResults(parsingResult, { partNumber: 1, source: 'practice', }) if (conversionResult.slotResults.length === 0) { return NextResponse.json( { error: 'No valid problems to add to session', }, { status: 400 } ) } const now = new Date() // Add timestamps to slot results and adjust slot indices const existingResults = (existingSession.results ?? []) as SlotResult[] const startSlotIndex = existingResults.length const slotResultsWithTimestamps: SlotResult[] = conversionResult.slotResults.map( (result, idx) => ({ ...result, slotIndex: startSlotIndex + idx, timestamp: now, }) ) // Merge new results with existing results const mergedResults = [...existingResults, ...slotResultsWithTimestamps] // Calculate updated stats const totalCount = mergedResults.length const correctCount = mergedResults.filter((r) => r.isCorrect).length // Update the existing session with the new problems await db .update(sessionPlans) .set({ results: mergedResults, // Update the completed timestamp since we added new work completedAt: now, // Mark as completed if it wasn't already status: 'completed', }) .where(eq(sessionPlans.id, existingSession.id)) // Update attachment to mark as processed await db .update(practiceAttachments) .set({ parsingStatus: 'approved', sessionCreated: true, createdSessionId: existingSession.id, // Reference to the session we added to }) .where(eq(practiceAttachments.id, attachmentId)) // Compute final stats const stats = computeParsingStats(parsingResult) return NextResponse.json({ success: true, sessionId: existingSession.id, problemCount: slotResultsWithTimestamps.length, totalSessionProblems: totalCount, correctCount, accuracy: totalCount > 0 ? correctCount / totalCount : null, skillsExercised: conversionResult.skillsExercised, stats, }) } catch (error) { console.error('Error approving and adding to session:', error) return NextResponse.json({ error: 'Failed to approve and add to session' }, { status: 500 }) } }) |